﻿// GL_Demo.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <cmath>
#pragma comment (lib, "glfw3.lib")
#pragma comment (lib, "opengl32.lib")


//全局变量
//[全局]创建宽高
int screen_width = 800;
int screen_height = 600;
//[全局]创建窗口（宽、高、窗口名、是否使用全盘模式、共享上下文）
auto window = glfwCreateWindow(screen_width, screen_height, "Computer Grephics", nullptr, nullptr);
//[全局]创建着色器程序
int shader_program=0;
//[全局]创建VAO
GLuint vertex_array_object;	
//[全局]创建VBO
GLuint vertex_buffer_object;
//[全局]创建EBO
unsigned int ElementBufferObject;

int GLInit();
int DataProcesser();
int Shader();
int Rendering();
void ProcessInput(GLFWwindow* window);

int main()
{
	int ResStus = EXIT_FAILURE;
	////////////初始化GLFW和GLAD////////////////////////////////////////////////////
	ResStus = GLInit();
	if (ResStus)
	{
		return ResStus;
	}

	////////////数据处理阶段	顶点数据定义->生成与绑定VAO和VBO->属性设置->解绑////
	////////////生成和绑定VBO和VAO，设置属性指针////////////////////////////////////
	ResStus = DataProcesser();
	if (ResStus)
	{
		return ResStus;
	}
	
	////////////着色器：顶点和片段着色器////////////////////////////////////////////
	ResStus = Shader();
	if (ResStus)
	{
		return ResStus;
	}

	////////////渲染////////////////////////////////////////////////////////////////
	ResStus = Rendering();
	if (ResStus)
	{
		return ResStus;
	}

	glfwTerminate();

	return ResStus;
}

////////////初始化GLFW和GLAD////////////////////////////////////////////////////
int GLInit()
{
	/*	初始化GLFW	*/
	glfwInit();//初始化GLFW
	//设定OpenGL版本号为3.3，分别对主次版本进行设置
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	//使用核心模式（无需向后兼容性）
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
	//是否显示在前面,如果是MacOSX系统，需要加上以下这行

#ifdef __APPLE__
	glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif // __APPLE__

	//不可改变窗口大小
	//glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

	/*	初始化窗口	*/
	//[全局]创建宽高
	//int screen_width = 1280;
	//int screen_height = 720;
	//[全局]创建窗口（宽、高、窗口名、是否使用全盘模式、共享上下文）
	window = glfwCreateWindow(screen_width, screen_height, "Computer Grephics", nullptr, nullptr);
	//窗口创建失败，输出错误提示，返回错误代码
	if (window == nullptr) {
		std::cout << "Failed to Create OpenGL Context" << std::endl;
		glfwTerminate();
		return EXIT_FAILURE;
	}
	//将窗口的上下文设置为当前线程的主上下文(OpenGL是状态机，OpenGL的状态就被叫做上下文)
	glfwMakeContextCurrent(window);

	/*	初始化GLAD	*/
	//初始化GLAD，加载OpenGL函数指针地址的函数
	if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
	{
		std::cout << "Failed to initialize GLAD" << std::endl;
		glfwTerminate();
		return EXIT_FAILURE;
	}

	/*	创建视口	*/
	//指定当前视口尺寸（前两个参数为左下角位置，后两个参数是渲染窗口宽、高）
	glViewport(0, 0, screen_width, screen_height );

	return 0;
}

////////////数据处理阶段	顶点数据定义->生成与绑定VAO和VBO->属性设置->解绑////
int DataProcesser()
{
////////////生成和绑定VBO和VAO，设置属性指针////////////////////////////////////
	/*	顶点数据定义	*/
	const float vertices[] = {
		//	---位置---			---颜色---
			-1.f,0.5f,0.f,		0.f,0.5f,0.f,	//左上0
			-0.5f,-0.5f,0.f,	1.f,0.f,0.f,	//左下1
			0.5f,-0.5f,0.f,		0.f,0.f,1.f,	//右下2
			0.f,0.5f,0.f,		0.f,1.f,0.f,	//正上3
			1.f,0.5f,0.f,		0.5f,0.f,0.5f	//右上4
	};
	
	//剔除背面
	/*glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);*/
	
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);//只画框线

	/*	生成与绑定VAO和VBO	*/
	//生成和绑定立方体的VAO和VBO
	//[全局]创建VAO
	//GLuint vertex_array_object;	//VAO
	glGenVertexArrays(1, &vertex_array_object);
	glBindVertexArray(vertex_array_object);
	//[全局]创建VBO
	//GLuint vertex_buffer_object;//VBO
	glGenBuffers(1, &vertex_buffer_object);
	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
	//将顶点数据绑定到当前默认的缓冲中
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//GL_STATIC_DRAW意思是三角形的位置不变

	/*	属性设置	*/
	//设置顶点属性指针
	//					(顶点着色器位置值,顶点属性是三分量的向量,顶点的数据类型,是否希望数据被标准化(0-1之间),步长--连续属性顶点间的间隔,数据偏移量)
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
	glEnableVertexAttribArray(0);
	glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));//颜色向量
	glEnableVertexAttribArray(1);

	
	//[全局]创建EBO
	//unsigned int ElementBufferObject;
	glGenBuffers(1, &ElementBufferObject);

	/*	解绑	*/
	//解绑VAO和VBO
	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);


	return 0;
}

////////////着色器：顶点和片段着色器////////////////////////////////////////////
int Shader()
{
	/*	着色器源码GLSL--类C	*/
	const char *vertex_shader_source =
		"#version 330 core\n"
		"layout (location=0)in vec3 aPos;\n"			//位置变量的属性位置值为0，in是输入变量
		"layout (location=1)in vec3 aColor;\n"			//颜色变量的属性位置值为1，in是输入变量
		"out vec4 vertexColor;\n"						//输出顶点颜色
		"void main()\n"
		"{\n"
		"	gl_Position = vec4(aPos,1.0);\n"
		"	vertexColor = vec4(aColor,1.0);\n"
		"}\n\0";
	const char *fragment_shader_source =
		"#version 330 core\n"		
		"in vec4 vertexColor;\n"						//接入顶点颜色
		"uniform vec4 ourColor;\n"						//接入uniform
		"out vec4 FragColor;\n"							//输出的颜色向量，out是输出变量
		"void main()\n"
		"{\n"
		"	FragColor = vertexColor;\n"
		//"	FragColor = ourColor;\n"
		"}\n";

	/*	生成并编译着色器	*/
	//顶点着色器
	int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
	glCompileShader(vertex_shader);
	int success;
	char info_log[512];
	//检查着色器是否成功编译，如果编译失败，打印错误信息
	glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
		std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << info_log << std::endl;
		return -1;
	}
	//片段着色器
	int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
	glCompileShader(fragment_shader);
	//检查着色器是否成功编译，如果编译失败，打印错误信息
	glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
	if (!success) {
		glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
		std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << info_log << std::endl;
		return -1;
	}

	/*	连接着色器到着色器程序	*/
	//[全局]创建着色器程序
	shader_program = glCreateProgram();
	glAttachShader(shader_program, vertex_shader);
	glAttachShader(shader_program, fragment_shader);
	glLinkProgram(shader_program);
	//连接顶点属性
	//glVertexAttribPointer(起始绘制数组(最大15),尺寸,类型,是否要将范围指定到-1~+1之间,每个数组间间隔为多少(3个float),第一笔的偏移量)
	//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);//位置向量
	//glEnableVertexAttribArray(0);
	//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)) );//颜色向量
	//glEnableVertexAttribArray(1);
	//检查着色器是否成功连接，如果连接失败，打印错误信息
	glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
	if (!success) {
		glGetProgramInfoLog(shader_program, 512, NULL, info_log);
		std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << info_log << std::endl;
		return -1;
	}

	/*	删除着色器	*/
	glDeleteShader(vertex_shader);
	glDeleteShader(fragment_shader);

	return 0;
}

////////////渲染////////////////////////////////////////////////////////////////
int Rendering()
{
	//EBO
	unsigned int indices[] = {
		0,1,3,
		3,1,2,
		3,2,4
	};
	while (!glfwWindowShouldClose(window)) {
		
		ProcessInput(window);
		
		//清空颜色缓冲(R,G,B,透明值)
		glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT);
		//使用变色三角形
		float timevalue = glfwGetTime();
		float greenValue = (sin(timevalue) / 2.0f)+0.5f;
		int vertexColorLocation = glGetUniformLocation(shader_program, "ourColor");
		glUseProgram(shader_program);
		glUniform4f(vertexColorLocation, 0, greenValue, 0, 1.0f);
		//使用着色器程序
		glUseProgram(shader_program);
		//绘制三角形
		glBindVertexArray(vertex_array_object);//绑定VAO
		//glDrawArrays(GL_TRIANGLES, 0, 6);						//VBO直接绘制三角形
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBufferObject);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
		glDrawElements(GL_TRIANGLES, 9, GL_UNSIGNED_INT, 0);	//EBO绘制三角形
		glBindVertexArray(0);//解除绑定


		//交换缓冲并且检查是否有出发时间（比如键盘输入、鼠标移动等）
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	return 0;
}

////////////输入////////////////////////////////////////////////////////////////
void ProcessInput(GLFWwindow* window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
}
